﻿#include "FITKOCCGraphObjectDatum.h"

// OCC
#include <gp_Pln.hxx>
#include <Geom_Plane.hxx>
#include <Geom_CartesianPoint.hxx>
#include <AIS_Plane.hxx>
#include <AIS_PlaneTrihedron.hxx>
#include <AIS_DisplayMode.hxx>
#include <Prs3d_Drawer.hxx>
#include <Prs3d_ShadingAspect.hxx>
#include <Prs3d_LineAspect.hxx>
#include <Prs3d_PointAspect.hxx>
#include <AIS_InteractiveContext.hxx>

// Graph
#include "FITKOCCAIS_Datum.h"

// Data
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoDatum.h"

namespace Exchange
{
    FITKOCCGraphObjectDatum::FITKOCCGraphObjectDatum(Interface::FITKAbsGeoDatum* datumData)
        : FITKOCCGraphObject3D(datumData)
    {
        if (!datumData)
        {
            return;
        }

        // Generate the interactive objects.
        generateShape();

        // Save the data type and initialize select modes.
        //@{
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeShape] = true;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeVertex] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeEdge] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeWire] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeFace] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeShell] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeSolid] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeCompound] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_MeshNode] = false;
        m_supportedSelectModeHash[CommonOCC::SelectMode::S_MeshElement] = false;

        Interface::FITKGeoEnum::FITKDatumType datumType = datumData->getDatumType();
        if (datumType >= Interface::FITKGeoEnum::FDTPoint && datumType < Interface::FITKGeoEnum::FDTLine)
        {
            m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeVertex] = true;
            m_graphInfo.Type = FITKOCCCommons::DatumPointGraph;
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTLine && datumType < Interface::FITKGeoEnum::FDTPlane)
        {
            m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeEdge] = true;
            m_graphInfo.Type = FITKOCCCommons::DatumLineGraph;
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTPlane)
        {
            m_supportedSelectModeHash[CommonOCC::SelectMode::S_ShapeFace] = true;
            m_graphInfo.Type = FITKOCCCommons::DatumPlaneGraph;
        }  
        //@}

        // Set the render layer to TopMost.
        // Need to change the selection layer to TopMost, too.
        //( Or the selection may be covered by the interactive object. )
        enableTopMost(true);

        // Save the fixed size flag.
        m_hasFixedSize = false;
    }

    FITKOCCGraphObjectDatum::~FITKOCCGraphObjectDatum()
    {
        // Don't need to delete pointers here.
        m_aisObj = nullptr;
    }

    void FITKOCCGraphObjectDatum::setViewMode(FITKOCCCommons::ShapeMeshViewMode type, bool visible)
    {
        Q_UNUSED(visible);

        // Get the datum data.
        Interface::FITKAbsGeoDatum* datumData = dynamic_cast<Interface::FITKAbsGeoDatum*>(_dataObj);
        if (!datumData || !m_aisObj)
        {
            return;
        }

        // Get the datum type.
        //@{
        Interface::FITKGeoEnum::FITKDatumType datumType = datumData->getDatumType();
        if (datumType >= Interface::FITKGeoEnum::FDTPoint && datumType < Interface::FITKGeoEnum::FDTLine)
        {
            // Datum point.
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTLine && datumType < Interface::FITKGeoEnum::FDTPlane)
        {
            // Datum line.
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTPlane)
        {
            // Datum plane.
            switch (type)
            {
            case Exchange::FITKOCCCommons::SMVM_Shade:
                m_aisObj->SetDisplayMode(AIS_DisplayMode::AIS_Shaded);
                break;
            case Exchange::FITKOCCCommons::SMVM_Wireframe:
                m_aisObj->SetDisplayMode(AIS_DisplayMode::AIS_WireFrame);
                break;
            default:
                return;
            }
        }
        else
        {
            return;
        }
        //@}

        // No use.
        FITKOCCGraphObject3D::setViewMode(type, true);
    }

    void FITKOCCGraphObjectDatum::update(bool forceUpdate)
    {
        // This grapg object need to be forced updated every time.
        Q_UNUSED(forceUpdate);

        Interface::FITKAbsGeoDatum* datumData = dynamic_cast<Interface::FITKAbsGeoDatum*>(_dataObj);
        if (!datumData)
        {
            return;
        }

        // Get the datum type.
        //@{
        Interface::FITKGeoEnum::FITKDatumType datumType = datumData->getDatumType();
        if (datumType >= Interface::FITKGeoEnum::FDTPoint && datumType < Interface::FITKGeoEnum::FDTLine)
        {
            // Datum point.
            FITKOCCAIS_ShapePoint* aisPoint = dynamic_cast<FITKOCCAIS_ShapePoint*>(m_aisObj);
            Interface::FITKAbsGeoDatumPoint* dpt = dynamic_cast<Interface::FITKAbsGeoDatumPoint*>(datumData);
            if (!dpt || !aisPoint)
            {
                return;
            }

            Handle(Geom_CartesianPoint) pt = Handle(Geom_CartesianPoint)::DownCast(aisPoint->Component());
            if (!pt)
            {
                return;
            }

            // Get the point parameters.
            double pos[3];
            dpt->getPosition(pos);

            // Replace the point.
            pt->SetCoord(pos[0], pos[1], pos[2]);

            aisPoint->update();
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTLine && datumType < Interface::FITKGeoEnum::FDTPlane)
        {
            // Datum line.
            FITKOCCAIS_ShapeLine* aisLine = dynamic_cast<FITKOCCAIS_ShapeLine*>(m_aisObj);
            Interface::FITKAbsGeoDatumLine* dl = dynamic_cast<Interface::FITKAbsGeoDatumLine*>(datumData);
            if (!dl || !aisLine)
            {
                return;
            }

            // Get the original color.
            Quantity_Color color;
            aisLine->Color(color);

            // Delete the line object, create a new one.
            if (m_context)
            {
                m_context->Remove(aisLine, false);
            }

            m_objs.clear();

            // Infinite line.
            if (dl->getIsInfinite())
            {
                // Get the line parameters.
                double pos[3], dir[3];
                dl->getInfiniteLine(pos, dir);

                gp_Dir gDir(dir[0], dir[1], dir[2]);

                // Replace the points.
                double pixelLen = 240.;
                gp_Pnt lenPt(gDir.X() * pixelLen * 0.5, gDir.Y() * pixelLen * 0.5, gDir.Z() * pixelLen * 0.5);

                // Re-create the ais object.
                Handle(Geom_CartesianPoint) pt1 = new Geom_CartesianPoint(gp_Pnt(-lenPt.X(), -lenPt.Y(), -lenPt.Z()));
                Handle(Geom_CartesianPoint) pt2 = new Geom_CartesianPoint(lenPt);
                aisLine = new FITKOCCAIS_ShapeLine(pt1, pt2);

                // Enable fixed pixel size.
                gp_Pnt center(pos[0], pos[1], pos[2]);
                Handle(Graphic3d_TransformPers) transPer = Graphic3d_TransformPers::FromDeprecatedParams(
                    Graphic3d_TransModeFlags::Graphic3d_TMF_ZoomPers, center);
                aisLine->SetTransformPersistence(transPer);
            }
            // Finite segment.
            else
            {
                // Get the line parameters.
                double pos[3], pos2[3];
                dl->getFiniteLine(pos, pos2);

                // Re-create the ais object.
                Handle(Geom_CartesianPoint) pt1 = new Geom_CartesianPoint(gp_Pnt(pos[0], pos[1], pos[2]));
                Handle(Geom_CartesianPoint) pt2 = new Geom_CartesianPoint(gp_Pnt(pos2[0], pos2[1], pos2[2]));
                aisLine = new FITKOCCAIS_ShapeLine(pt1, pt2);
            }

            // Update the color and shape.
            aisLine->SetColor(color);
            aisLine->update();

            m_aisObj = aisLine;

            // Save the graph info.
            FITKAIS_ObjectBase* aisBase = dynamic_cast<FITKAIS_ObjectBase*>(aisLine);
            if (aisBase)
            {
                aisBase->setGraphObject(this);
                aisBase->setDataObjectId(datumData->getDataObjectID());
            }

            // Create handle.    
            if (m_aisObj)
            {
                Handle(AIS_InteractiveObject) datumHandle(m_aisObj);
                addInteractiveObj(datumHandle);
            }

            // Change the render layer.
            enableTopMost(true);
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTPlane)
        {
            // Datum plane.
            FITKOCCAIS_ShapePlane* aisPln = dynamic_cast<FITKOCCAIS_ShapePlane*>(m_aisObj);
            Interface::FITKAbsGeoDatumPlane* dp = dynamic_cast<Interface::FITKAbsGeoDatumPlane*>(datumData);
            if (!dp || !aisPln)
            {
                return;
            }

            // Get the original color.
            Quantity_Color color;
            aisPln->Color(color);

            // Delete the plane object, create a new one.
            if (m_context)
            {
                m_context->Remove(aisPln, false);
            }

            m_objs.clear();

            // Get the plane parameters.
            double pos[3], nor[3], up[3];
            dp->getPosition(pos);
            dp->getNormal(nor);
            dp->getUp(up);

            gp_Pnt zero(0., 0., 0.);
            gp_Pnt center(pos[0], pos[1], pos[2]);

            // Create the plane and interactive object.
            gp_Ax3 ax3(zero,
                gp_Dir(nor[0], nor[1], nor[2]),
                gp_Dir(up[0], up[1], up[2]));
            Handle(Geom_Plane) pln = new Geom_Plane(ax3);

            aisPln = new FITKOCCAIS_ShapePlane(pln);
            m_aisObj = aisPln;

            // Fixed pixel size.
            Handle(Graphic3d_TransformPers) transPer = Graphic3d_TransformPers::FromDeprecatedParams(
                Graphic3d_TransModeFlags::Graphic3d_TMF_ZoomPers, center);
            aisPln->SetTransformPersistence(transPer);

            // Update the color and shape.
            aisPln->SetColor(color);
            aisPln->update();

            m_aisObj = aisPln;

            // Save the graph info.
            FITKAIS_ObjectBase* aisBase = dynamic_cast<FITKAIS_ObjectBase*>(aisPln);
            if (aisBase)
            {
                aisBase->setGraphObject(this);
                aisBase->setDataObjectId(datumData->getDataObjectID());
            }

            // Create handle.    
            if (m_aisObj)
            {
                Handle(AIS_InteractiveObject) datumHandle(m_aisObj);
                addInteractiveObj(datumHandle);
            }

            // Change the render layer.
            enableTopMost(true);
        }
        else
        {
            return;
        }
        //@}

        // Update interactive object if necessary.
        updateInteractiveObjs();

        // Update base.
        FITKOCCGraphObject3D::update(forceUpdate);
    }

    void FITKOCCGraphObjectDatum::updateInteractiveObjs()
    {
        if (!m_aisObj)
        {
            m_aisObj->SetToUpdate(true);
        }
    }

    void FITKOCCGraphObjectDatum::setColor(QColor color)
    {
        if (!m_aisObj || !color.isValid())
        {
            return;
        }

        m_aisObj->SetColor(FITKOCCCommons::QColorToQuan(color));
    }

    void FITKOCCGraphObjectDatum::setEnableTransparency(bool isOn)
    {
        if (!m_aisObj)
        {
            return;
        }

        // Default 0.8.
        m_aisObj->SetTransparency(isOn ? 0.8 : 0.);
    }

    void FITKOCCGraphObjectDatum::setLineWidth(double size)
    {
        if (!m_aisObj)
        {
            return;
        }

        // Get the drawer.
        Handle(Prs3d_Drawer) properties = m_aisObj->Attributes();

        // Line and wire properties.
        properties->LineAspect()->SetWidth(size);
        properties->WireAspect()->SetWidth(size);
        properties->FaceBoundaryAspect()->SetWidth(size);
    }

    void FITKOCCGraphObjectDatum::highlight(bool update)
    {
        // Save the highlight flag.
        FITKOCCGraphObject3D::highlight(update);
    }

    void FITKOCCGraphObjectDatum::disHighlight(bool update)
    {
        // Save the highlight flag.
        FITKOCCGraphObject3D::disHighlight(update);
    }

    void FITKOCCGraphObjectDatum::generateShape()
    {
        Interface::FITKAbsGeoDatum* datumData = dynamic_cast<Interface::FITKAbsGeoDatum*>(_dataObj);
        if (!datumData)
        {
            return;
        }

        // Get the datum type.
        //@{
        Interface::FITKGeoEnum::FITKDatumType datumType = datumData->getDatumType();
        if (datumType >= Interface::FITKGeoEnum::FDTPoint && datumType < Interface::FITKGeoEnum::FDTLine)
        {
            // Datum point.
            Interface::FITKAbsGeoDatumPoint* dpt = dynamic_cast<Interface::FITKAbsGeoDatumPoint*>(datumData);
            if (!dpt)
            {
                return;
            }

            // Get the point parameters.
            double pos[3];
            dpt->getPosition(pos);

            // Create the point and interactive object.
            gp_Pnt center(pos[0], pos[1], pos[2]);
            Handle(Geom_CartesianPoint) pt = new Geom_CartesianPoint(center);
            FITKOCCAIS_ShapePoint* aisPoint = new FITKOCCAIS_ShapePoint(pt);
            m_aisObj = aisPoint;
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTLine && datumType < Interface::FITKGeoEnum::FDTPlane)
        {
            // Datum line.
            Interface::FITKAbsGeoDatumLine* dl = dynamic_cast<Interface::FITKAbsGeoDatumLine*>(datumData);
            if (!dl)
            {
                return;
            }

            // Infinite line.
            if (dl->getIsInfinite())
            {
                // Get the line parameters.
                double pos[3], dir[3];
                dl->getInfiniteLine(pos, dir);
                gp_Dir gDir(dir[0], dir[1], dir[2]);

                // Replace the points.
                double pixelLen = 240.;
                gp_Pnt lenPt(gDir.X() * pixelLen * 0.5, gDir.Y() * pixelLen * 0.5, gDir.Z() * pixelLen * 0.5);
                Handle(Geom_CartesianPoint) pt1 = new Geom_CartesianPoint(gp_Pnt(-lenPt.X(), -lenPt.Y(), -lenPt.Z()));
                Handle(Geom_CartesianPoint) pt2 = new Geom_CartesianPoint(lenPt);
                FITKOCCAIS_ShapeLine* aisLine = new FITKOCCAIS_ShapeLine(pt1, pt2);
                m_aisObj = aisLine;

                // Enable fixed pixel size.
                gp_Pnt center(pos[0], pos[1], pos[2]);
                Handle(Graphic3d_TransformPers) transPer = Graphic3d_TransformPers::FromDeprecatedParams(
                    Graphic3d_TransModeFlags::Graphic3d_TMF_ZoomPers, center);
                aisLine->SetTransformPersistence(transPer);
            }
            // Finite segment.
            else
            {
                // Get the line parameters.
                double pos[3], pos2[3];
                dl->getFiniteLine(pos, pos2);

                // Create the points and interactive object.
                Handle(Geom_CartesianPoint) pt1 = new Geom_CartesianPoint(gp_Pnt(pos[0], pos[1], pos[2]));
                Handle(Geom_CartesianPoint) pt2 = new Geom_CartesianPoint(gp_Pnt(pos2[0], pos2[1], pos2[2]));
                FITKOCCAIS_ShapeLine* aisLine = new FITKOCCAIS_ShapeLine(pt1, pt2);
                m_aisObj = aisLine;
            }
        }
        else if (datumType >= Interface::FITKGeoEnum::FDTPlane)
        {
            // Datum plane.
            Interface::FITKAbsGeoDatumPlane* dp = dynamic_cast<Interface::FITKAbsGeoDatumPlane*>(datumData);
            if (!dp)
            {
                return;
            }

            // Get the plane parameters.
            double pos[3], nor[3], up[3];
            dp->getPosition(pos);
            dp->getNormal(nor);
            dp->getUp(up);

            gp_Pnt zero(0., 0., 0.);
            gp_Pnt center(pos[0], pos[1], pos[2]);

            // Create the plane and interactive object.
            gp_Ax3 ax3(zero,
                gp_Dir(nor[0], nor[1], nor[2]),
                gp_Dir(up[0], up[1], up[2]));
            Handle(Geom_Plane) pln = new Geom_Plane(ax3);

            FITKOCCAIS_ShapePlane* aisPln = new FITKOCCAIS_ShapePlane(pln);
            m_aisObj = aisPln;

            // Fixed pixel size.
            Handle(Graphic3d_TransformPers) transPer = Graphic3d_TransformPers::FromDeprecatedParams(
                Graphic3d_TransModeFlags::Graphic3d_TMF_ZoomPers, center);
            aisPln->SetTransformPersistence(transPer);
        }
        else
        {
            return;
        }
        //@}

        // Save the graph info.
        FITKAIS_ObjectBase* aisBase = dynamic_cast<FITKAIS_ObjectBase*>(m_aisObj);
        if (aisBase)
        {
            aisBase->setGraphObject(this);
            aisBase->setDataObjectId(datumData->getDataObjectID());
        }

        // Create handle.    
        if (m_aisObj)
        {
            Handle(AIS_InteractiveObject) datumHandle(m_aisObj);
            addInteractiveObj(datumHandle);
        }
    }
}   // namespace Exchange